package com.szq.netty.future;

import com.szq.netty.msg.Request;
import com.szq.netty.msg.Response;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener;

import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * @ClassName: SyncWrite
 * @Description:
 * @Author: szq
 * @Date: 2021-11-17 09:30:20
 */
public class SyncWrite {

    public Response writeAndSync(final Channel channel, final Request request, final long timeout) throws Exception{
        if (channel == null){
            throw new NullPointerException("channel");
        }

        if (request == null){
            throw new NullPointerException("request");
        }

        if (timeout <= 0){
            throw new IllegalArgumentException("timeout <= 0");
        }

        String requestId = UUID.randomUUID().toString();
        request.setRequestId(requestId);

        WriteFuture<Response> future = new SyncWriteFuture(request.getRequestId());
        SyncWriteMap.syncKey.put(request.getRequestId(), future);

        Response response = doWriteAndSync(channel, request, timeout, future);

        SyncWriteMap.syncKey.remove(request.getRequestId());
        return response;
    }

    private Response doWriteAndSync(final Channel channel, final Request request, final long timeout, final WriteFuture<Response> future) throws Exception {
        channel.writeAndFlush(request).addListener((ChannelFutureListener) channelFuture -> {
           future.setWriteResult(channelFuture.isSuccess());
           future.setCause(channelFuture.cause());
           //失败移除
            if (!future.isWriteSuccess()){
                SyncWriteMap.syncKey.remove(future.requestId());
            }
        });

        Response response = future.get(timeout, TimeUnit.MILLISECONDS);
        if (response == null){
            if (future.isTimeout()){
                throw new TimeoutException();
            }else{
                //write exception
                throw new Exception(future.cause());
            }
        }
        return response;
    }
}
